#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <mutex>
#include <thread>
#include <functional>
using namespace std;
namespace lnb
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
		{
			_ptr = ptr;
		}

		auto_ptr(auto_ptr& atptr)
		{
			_ptr = atptr._ptr;
			atptr._ptr = nullptr;
		}

		auto_ptr& operator=(auto_ptr<T>& atptr)
		{
			if (atptr._ptr != _ptr)
			{
				delete _ptr;
				_ptr = atptr._ptr;
				atptr._ptr = nullptr;
			}
			return *this;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			
			return _ptr;
		}


		~auto_ptr()
		{
			delete _ptr;
		}

	private:
		T* _ptr;
	};

	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
		{
			_ptr = ptr;
		}

		unique_ptr(const unique_ptr& atptr) = delete;

		unique_ptr& operator=(unique_ptr<T>& atptr) = delete;

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{

			return _ptr;
		}


		~unique_ptr()
		{
			delete _ptr;
		}

	private:
		T* _ptr;
	};
	
	template<class T,class Del=function<void(void)>>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			, _count(new int(1))
			, _mtx(new mutex)
		{

		}

		shared_ptr(const shared_ptr& sptr)
		{
			_ptr = sptr._ptr;
			_count = sptr._count;
			_mtx = sptr._mtx;
			AddCount();

		}

		shared_ptr& operator=(const shared_ptr& sptr)
		{
			if (this != &sptr)
			{
				Release();
				_ptr = sptr._ptr;
				_count = sptr._count;
				_mtx = sptr._mtx;
				AddCount();

			}

			return *this;
		}


		void AddCount()
		{
			_mtx->lock();
			(*(_count))++;
			_mtx->unlock();
		}

		T& operator*()
		{
			if (_ptr == nullptr)
			{
				throw "nullptr";
			}
			return *_ptr;
		}

		T* operator->()
		{
			if (_ptr == nullptr)
			{
				throw "nullptr";
			}
			return _ptr;
		}

		T* get()
		{
			return _ptr;
		}

		template<class D>
		shared_ptr(T* ptr, D del)
			:_ptr(ptr),
			_del(del),
			_count(new int(1)),
			_mtx(new  mutex)
		{}
		template<class D>
		shared_ptr(const shared_ptr<T,D>& sptr)
		{	_ptr = sptr._ptr;
		_count = sptr._count;
		_mtx = sptr._mtx;
		AddCount();
	}

		int use_count()
		{
			return *_count;
		}

		void Release()
		{
			//cout << "into" << endl;
			bool flag=false;
			_mtx->lock();
			if (--( * _count) == 0)
			{
				//cout << "release already" << endl;
				flag =true;
				_del(_ptr);
				delete _count;
			}
			_mtx->unlock();
			if (flag)
			{
				delete _mtx;
			}


		}

		~shared_ptr()
		{
			Release();
		}

	private:
		T* _ptr;
		int* _count;
		mutex* _mtx;
		function<void(T*)> _del = [](T* ptr) {
			delete ptr;
		};
	};

	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr(T* ptr=nullptr)
		{
			_ptr = ptr;
		}

		weak_ptr(shared_ptr<T>& atptr)
		{
			_ptr = atptr.get();
		}
		weak_ptr& operator=(shared_ptr<T>& atptr)
		{
			_ptr = atptr.get();
			return *this;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{

			return _ptr;
		}
	private:
		T* _ptr;
	};


}



